package org.yelongframework.zip.file;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.yelongframework.commons.io.FileNameUtil;
import org.yelongframework.core.util.function.io.ByteArraySupplier;
import org.yelongframework.lang.Nullable;
import org.yelongframework.zip.entry.DirZipEntry;
import org.yelongframework.zip.entry.FileZipEntry;
import org.yelongframework.zip.entry.ZipEntry;

/**
 * 压缩文件
 * 
 * @since 1.0.0
 */
public abstract class ZipFile {

	/** 压缩项集合 */
	private final List<ZipEntry> zipEntrys;

	// ==================================================构造器==================================================

	public ZipFile() {
		this(new ArrayList<>());
	}

	/**
	 * @param zipEntrys 压缩项集合
	 */
	public ZipFile(List<ZipEntry> zipEntrys) {
		this.zipEntrys = zipEntrys;
	}

	/**
	 * @param zipFile 压缩文件
	 */
	public ZipFile(ZipFile zipFile) {
		this.zipEntrys = zipFile.getZipEntrys();
	}

	// ==================================================添加文件==================================================

	public void addFileEmpty(String name) {
		addZipEntry(new FileZipEntry(name, null));
	}

	/**
	 * 简单的添加一个压缩文件项。在压缩包内的文件名称就是文件的名称
	 * 
	 * @param file 被压缩的文件
	 */
	public void addFileSimple(File file) {
		addFile(file.getName(), file);
	}

	/**
	 * 简单的添加一个压缩文件项。在压缩包内的文件名称就是文件的全路径名称
	 * 
	 * @param file 被压缩的文件
	 */
	public void addFileFullPath(File file) {
		addFile(file.getAbsolutePath(), file);
	}

	/**
	 * 添加一个压缩文件项
	 * 
	 * @param name 在压缩包内的文件名称（文件全路径，可以通过文件分隔符进行分割）
	 * @param file 被压缩的文件
	 */
	public void addFile(String name, File file) {
		addFile(name, () -> FileUtils.readFileToByteArray(file));
	}

	/**
	 * 添加一个压缩文件项
	 * 
	 * @param name         在压缩包内的文件名称（文件全路径，可以通过文件分隔符进行分割）
	 * @param dataSupplier 数据供应商
	 */
	public void addFile(String name, @Nullable ByteArraySupplier dataSource) {
		addZipEntry(new FileZipEntry(name, dataSource));
	}

	// ==================================================添加文件夹==================================================

	/**
	 * 添加一个空目录
	 * 
	 * @param name 目录名称
	 * @throws IOException 流异常
	 */
	public void addDirectoryEmpty(String name) throws IOException {
		addZipEntry(new DirZipEntry(name));
	}

	/**
	 * 添加一个压缩文件项。在压缩包内的文件名称为文件的名称
	 * 
	 * @param file 被压缩的文件目录
	 * @throws IOException 流异常
	 */
	public void addDirectoryListFilesSimple(File file) throws IOException {
		addDirectoryListFiles(file.getName(), file);
	}

	/**
	 * 添加一个压缩文件项。在压缩包内的文件名称为文件的名称
	 * 
	 * @param file       被压缩的文件目录
	 * @param fileFilter 文件过滤器
	 * @throws IOException 流异常
	 */
	public void addDirectoryListFilesSimple(File file, @Nullable FileFilter fileFilter) throws IOException {
		addDirectoryListFiles(file.getName(), file, fileFilter);
	}

	/**
	 * 添加一个压缩文件项。在压缩包内的文件名称为文件的全路径名称
	 * 
	 * @param file 被压缩的文件目录
	 * @throws IOException 流异常
	 */
	public void addDirectoryListFilesFullPath(File file) throws IOException {
		addDirectoryListFiles(file.getAbsolutePath(), file);
	}

	/**
	 * 添加一个压缩文件项。在压缩包内的文件名称为文件的全路径名称
	 * 
	 * @param file       被压缩的文件目录
	 * @param fileFilter
	 * @throws IOException
	 */
	public void addDirectoryListFilesFullPath(File file, @Nullable FileFilter fileFilter) throws IOException {
		addDirectoryListFiles(file.getAbsolutePath(), file, fileFilter);
	}

	/**
	 * 添加一个压缩文件项
	 * 
	 * @param name 在压缩包内的文件名称（文件全路径，可以通过文件分隔符进行分割）
	 * @param file 被压缩的文件
	 * @throws IOException 流异常
	 */
	public void addDirectoryListFiles(String name, File file) throws IOException {
		addDirectoryListFiles(name, file, null);
	}

	/**
	 * 添加一个压缩文件项
	 * 
	 * @param name       在压缩包内的文件名称（文件全路径，可以通过文件分隔符进行分割）
	 * @param file       被压缩的文件
	 * @param fileFilter 文件过滤器
	 * @throws IOException 流异常
	 */
	public void addDirectoryListFiles(@Nullable String name, File file, @Nullable FileFilter fileFilter)
			throws IOException {
		Collection<File> listFiles = FileUtils.listFiles(file, null, true);
		if (null != fileFilter) {
			listFiles.removeIf(fileFilter::accept);
		}
		for (File childFile : listFiles) {
			if (childFile.isDirectory()) {
				addDirectoryEmpty(childFile.getName());
			} else if (childFile.isFile()) {
				String absolutePath = childFile.getAbsolutePath();
				absolutePath = absolutePath.substring(file.getAbsolutePath().length() + 1);
				String childName;
				if (StringUtils.isBlank(name)) {
					childName = absolutePath;
				} else {
					childName = FileNameUtil.getFilePath(name, absolutePath);
				}
				addFile(childName, childFile);
			}
		}
	}

	// ==================================================添加压缩文件项==================================================

	/**
	 * 添加一个压缩文件项
	 * 
	 * @param zipEntry 压缩文件项
	 */
	public void addZipEntry(ZipEntry zipEntry) {
		zipEntrys.add(zipEntry);
	}

	/**
	 * 添加多个压缩文件项
	 * 
	 * @param zipEntrys 压缩文件项集合
	 */
	public void addZipEntrys(List<ZipEntry> zipEntrys) {
		this.zipEntrys.addAll(zipEntrys);
	}

	// ==================================================判断==================================================

	/**
	 * @return 压缩的文件项是否为空
	 */
	public boolean isEmpty() {
		return zipEntrys.isEmpty();
	}

	// ==================================================获取压缩文件项==================================================

	/**
	 * @param name 压缩项名称
	 * @return 压缩文件项
	 */
	public ZipEntry getZipEntry(String name) {
		return getZipEntrys().stream().filter(x -> x.getName().equals(name)).findFirst().get();
	}

	// ==================================================获取压缩文件项集合==================================================

	/**
	 * @return 压缩文件项集合
	 */
	public List<ZipEntry> getZipEntrys() {
		return zipEntrys;
	}

}
